import re
import time
from imaplib import IMAP4, IMAP4_SSL
from poplib import POP3, POP3_SSL
from typing import cast
from email import message_from_string
from email.parser import HeaderParser
from email.header import decode_header
from email.utils import mktime_tz, parsedate_tz
from collections import namedtuple

# domain --> (protocol, host, port)
email_api_info = {
    'dt-vpn.top': ('imaps', 'imaps.dt-vpn.top', 993),
    'mimail.life': ('imaps', 'imaps.dt-vpn.top', 993),
    'mimail.pw': ('imaps', 'imaps.dt-vpn.top', 993),
    'penguinmail.store': ('imaps', 'imaps.dt-vpn.top', 993),
    'vip-mail.world': ('imaps', 'imaps.dt-vpn.top', 993),
    'rhinomail.top': ('imaps', 'imaps.dt-vpn.top', 993),
    'ayeayemail.com': ('imaps', 'imaps.dt-vpn.top', 993),
    'vibrantmail.world': ('imaps', 'imaps.dt-vpn.top', 993),
    'sunnymail.top': ('imaps', 'imaps.dt-vpn.top', 993),
    'fortunemail.top': ('imaps', 'imaps.dt-vpn.top', 993),
    'noblemail.top': ('imaps', 'imaps.dt-vpn.top', 993),
    # 'outlook.com': ('imaps', 'outlook.office365.com', 993),
    'outlook.com': ('pop3', 'outlook.office365.com', 995),
    # 'hotmail.com': ('imaps', 'outlook.office365.com', 993)
    'hotmail.com': ('pop3', 'outlook.office365.com', 995),
}

Email = namedtuple('Email', 'title body date')


def get_domain(email: str):
    return email[email.index('@') + 1:]


def fetch(email: str, password: str, index: int = 0):
    domain = get_domain(email)
    info = email_api_info.get(domain)
    if info:
        protocol, host, port = info[0], info[1], info[2]
        if protocol.startswith('pop3'):
            return pop3(email, password, protocol, host, port, index)
        elif protocol.startswith('imap'):
            return imap(email, password, protocol, host, port, index)
    raise NotImplementedError


def pop3(email: str, password: str, protocol, host, port, index: int = 0):
    if protocol == "pop3s":
        server = POP3_SSL(host, port)
    else:
        server = POP3(host, port)

    server.user(email)
    server.pass_(password)
    _, items, _ = server.list()
    item = items[len(items) - index - 1]
    uid, _ = item.split()
    _, data, _ = server.retr(int(uid))
    return b'\n'.join(data).decode('utf-8')


def imap(email: str, password: str, protocol, host, port, index: int = 0):
    if protocol == "imaps":
        imap = IMAP4_SSL(host, port)
    else:
        imap = IMAP4(host, port)

    with imap as M:
        # 登录
        M.login(email, password)
        # 选取文件夹，默认inbox
        M.select()
        # 获取所有邮件id
        _, data = M.search(None, 'ALL')

        uids = list(reversed(data[0].split()))
        # 获取邮件
        if uids:
            _, data = M.fetch(uids[index], '(RFC822)')
            if data and data[0]:
                return cast(bytes, data[0][1]).decode('utf-8')
        M.logout()
    return None


def get_email(email: str, password: str, index: int = 0, time_limit=None):
    msg = fetch(email, password, index)
    header = HeaderParser().parsestr(msg)
    date = header.get('Date')
    if time_limit:
        time_tz_tuple = parsedate_tz(date)
        received_time = mktime_tz(time_tz_tuple)
        if time.time() - received_time > time_limit:
            return None

    title = ''
    body = ''
    msg_parts = message_from_string(msg)
    for part in msg_parts.walk():
        if 'Subject' in part:
            data, charset = decode_header(part['Subject'])[0]
            if charset:
                title = data.decode(charset)
            else:
                title = data

        if not part.get_content_type().startswith('multipart'):
            payload = part.get_payload(decode=True)
            if payload:
                try:
                    body += payload.decode('utf-8') + '\n\n'
                except UnicodeDecodeError:
                    try:
                        body += str(payload) + '\n\n'
                    except:
                        pass

    return Email(title, body, date)


# only support digits
# e.g. xxx-xxx is your verify code
def get_code(email: str, password: str, index: int = 0, time_limit=None):
    msg = get_email(email, password, index, time_limit)
    m = re.search(r'\d[\d-]{3,}\d', msg.title)
    if m:
        code = m.group()
        print(f'{email}, Recevie verify code: {code}')
        return code

    m = re.search(r'\d[\d-]{3,}\d', msg.body)
    if m:
        code = m.group()
        print(f'{email}, Recevie verify code:{code}')
        return code
    return None


if __name__ == '__main__':
    # code = get_code('juliannicfr@hotmail.com', 'Xjjj5Bp5')
    # print(code)

    email = get_email('yodjs96ihl4@dt-vpn.top', 'shuyuan008')
